对于很多项目,使用既定的约定和使用合理的默认值是需要的,Spring Web MVC现在已经明确支持了约定大于配置。这意味着当你建立一系列命名约定和类似的东西,你可以大幅度减少所需的处理器映射和视图解析器的配置以及ModelAndView
的实例等等。这对快速开发有很大的好处,并且当你在生产中更改代码库,它还会给你来带一定程度上的一致性。
约定大于配置的支持主要是针对MVC的三大核心区域:模型,视图和控制器。
ControllerClassNameHandlerMapping
类是HandlerMapping
的实现,约定用来检测请求URL和处理这个请求的Controller
实现的映射。
考虑下面这个简单的Controller
的实现,尤其注意这个类的名字。
public class ViewShoppingCartController implements Controller {
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) {
// the implementation is not hugely important for this example...
}
}
对应的Spring Web MVC配置文件对应的片段:
<bean class="org.springframework.web.servlet.mvc.support.ControllerClassNameHandlerMapping"/>
<bean id="viewShoppingCart" class="x.y.z.ViewShoppingCartController">
<!-- inject dependencies as required... -->
</bean>
ControllerClassNameHandlerMapping
会查找所有定义在应用程序上下文中的处理器(或Controller
),并根据它的名字定义映射关系。因此,ViewShoppingCartController
映射了/viewshoppingcart*
的请求。
让我们通过更多的例子来熟悉这个观点。(注意URL中都是小写,而在Controller
类的名字中则是驼峰的规则。)
WelcomeController
映射/welcome*
HomeController
映射/home*
IndexController
映射/index*
RegisterController
映射/register*
对于使用MultiActionController
处理器类的情况,映射的规则稍微复杂一些。假设下面的Controller
都是MultiActionController
的实现:
AdminController
映射了/admin/*
CatalogController
映射了/catalog/*
如果你的Controller
的名字遵循了xxxController
的约定,ControllerClassNameHandlerMapping
让你免去了定义和维护一个SimpleUrlHandlerMapping
(或类似的工作)。
ControllerClassNameHandlerMapping
类继承了AbstractHandlerMapping
基类,因此你可以定义HandlerInterceptor
实例和像定义HandlerMapping
的实现一样定义其他任何你需要的。
ModelMap
类本质上是Map
,可以添加需要在View
上展示的对象,它也遵循通用的命名约束。考虑下面的Controller
实现;注意对象被添加到ModelAndView
而没有指定关联的名称。
public class DisplayShoppingCartController implements Controller {
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) {
List cartItems = // get a List of CartItem objects
User user = // get the User doing the shopping
ModelAndView mav = new ModelAndView("displayShoppingCart"); <-- the logical view name
mav.addObject(cartItems); <-- look ma, no name, just the object
mav.addObject(user); <-- and again ma!
return mav;
}
}
ModelAndView
类使用了ModelMap
类,它是一个自定义的Map
实现,当对象被添加时,会自动为添加的对象生成key。检测被添加的对象策略是,像User
这样的对象,会使用其简单类名。下面是被添加到ModelMap
中的对象所生成的名字的例子。
添加x.y.User
实例会生成user
的名字。
添加x.y.Registration
实例会生成registration
的名字。
添加x.y.Foo
实例会生成foo
的名字。
添加java.util.HashMap
实例会生成hashMap
的实例。这种情况下,你可能会想要直接指定名字,因为hasmMap
不够直观。
* 添加null
会引起IllegalArgumentException
异常。如果你添加的对象有可能为null
,那么你也需要直接指定名字。
什么,没有自动的复数化?
Spring Web MVC的约定大于配置的支持并不支持自动的复数化。也就是说,当你添加了一个List
的Person
到ModelAndView
,它不会自动生成people
的名字。
这是在一番争议之后决定的,“Principle of Least Surprise”最终赢了。
当添加Set
或List
后,生成名字的策略是检查容器用第一个对象的简单类名并在之后加上List
。对于数组来说也是一样的,但是数组没有必要检查内容。下面的一些例子会让你对使用容器时产生名字情况更清晰:
有零个或多个x.y.User
元素的x.y.User[]
数组产生的名字是userList
。
有零个或多个x.y.User
元素的x.y.Foo[]
数组产生的名字是fooList
。
有一个或多个x.y.User
元素的java.util.ArrayList
产生的名字userList
。
有一个或多个x.y.Foo
元素的java.util.HashSet
产生的名字是fooList
。
* 一个空的java.util.ArrayList
将不会被添加(事实上,addObject(..)
的调用本质上是一个no-op)。
在没有显示的指定逻辑视图名时RequestToViewNameTranslator
接口会检测逻辑的View
名。它只有一个实现,DefaultRequestToViewNameTranslator
类。
DefaultRequestToViewTranslator
映射了请求URL到逻辑视图名,比如:
public class RegistrationController implements Controller {
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) {
// process the request...
ModelAndView mav = new ModelAndView();
// add data as necessary to the model...
return mav;
// notice that no View or logical view name has been set
}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- this bean with the well known name generates view names for us -->
<bean id="viewNameTranslator"
class="org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator"/>
<bean class="x.y.RegistrationController">
<!-- inject dependencies as necessary -->
</bean>
<!-- maps request URLs to Controller names -->
<bean class="org.springframework.web.servlet.mvc.support.ControllerClassNameHandlerMapping"/>
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>
</beans>
注意handleRequest(..)
的实现返回的ModelAndView
没有设置View
或是逻辑视图名。DefaultRequestToViewNameTranslator
的任务就是根据请求的URL产生逻辑视图名。对于上面的例子,RegistrationController
并且结合使用了ControllerClassNameHandlerMapping
的情况,URL为http://localhost/registeration.html
会使DefaultRequestToViewNameTranslator
产生registeration
的视图名。之后这个视图名会被InternalResourceViewResolver
解析成/WEB-INF/jsp/registeration.jsp
。
你不需要显式的定义
DefaultRequestToViewNameTranslator
。如果你喜欢DefaultRequestToViewNameTranslator
的默认设置,你可以依赖Spring Web MVCDispatcherServlet
在没有显式配置的情况下,初始化这个类的实例。
当然。如果你需要改变这个默认设置,那么你就要显式配置你自己的DefaultRequestToViewNameTranslator
。关于DefaultRequestToViewNameTranslator
各种属性的配置详细信息信息,请参考文档。